package com.zhx.mvcframwork.servlet;

import com.zhx.annotation.ZHXAutowired;
import com.zhx.annotation.ZHXController;
import com.zhx.annotation.ZHXRequestMapping;
import com.zhx.annotation.ZHXService;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

public class DispatcherServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;
    Map<String, Object> ioc = new HashMap<String, Object>();
    Map<String, Object> handlermapping = new HashMap<String, Object>();
    List<String> classNames = new ArrayList<String>();
    Properties properties = new Properties();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        try {
            //执行url对应的方法
            doDispatch(req, resp);
        } catch (IOException e) {
            e.printStackTrace();
            resp.getWriter().write("500");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException, InvocationTargetException, IllegalAccessException {

        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replaceAll(contextPath, "").replaceAll("/+", "/");

        if (!handlermapping.containsKey(url)) {
            resp.getWriter().write("404");
            return;
        }
        //获取url对应的方法
        Method method = (Method) handlermapping.get(url);
        //获取方法的参数
        Map<String, String[]> parameterMap = req.getParameterMap();
        //获取方法对应的类名
        String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
        //Todo 待优化参数接收
        method.invoke(ioc.get(beanName), new Object[]{parameterMap.get("userName")[0]});

    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        //加载配置文件
        doLoadConfig(config.getInitParameter("contextConfig"));
        //扫描相关的类
        doScanner(properties.getProperty("contextConfig"));
        //实例化类放到ioc容器中
        doInstance();
        //属性注入
        doAutowired();
        //url映射方法
        doHanderMapping();

    }

    private void doHanderMapping() {
        if (ioc.isEmpty()) {
            return;
        }
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            Class c = entry.getValue().getClass();
            if (!c.isAnnotationPresent(ZHXController.class)) {
                continue;
            }
            //当Controller层加了ZHXRequestMapping全局url则获取到
            String baseUrl = "";
            if (c.isAnnotationPresent(ZHXRequestMapping.class)) {
                ZHXRequestMapping requestMapping = (ZHXRequestMapping) c.getAnnotation(ZHXRequestMapping.class);
                baseUrl = requestMapping.value();
            }
            //循环ZHXController注释的类中的所有方法
            for (Method method : c.getMethods()) {
                if (!method.isAnnotationPresent(ZHXRequestMapping.class)) {
                    continue;
                }
                ZHXRequestMapping requestMapping = method.getAnnotation(ZHXRequestMapping.class);
                //如果没写斜杠,就加上
                String url = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+", "/");
                handlermapping.put(url, method);
                System.out.println(url + "注册成功!");
            }
        }
    }

    private void doAutowired() {
        if (ioc.isEmpty()) {
            return;
        }

        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            //获取bean 中的字段
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : fields) {
                if (!field.isAnnotationPresent(ZHXAutowired.class)) {
                    continue;
                }
                ZHXAutowired zhxAutowired = field.getAnnotation(ZHXAutowired.class);
                //获取注解修饰的自定义类名,如果空就用原类型名
                String beanName = zhxAutowired.value().trim();
                if ("".equals(beanName)) {
                    beanName = field.getType().getName();
                }
                //解决private字段不能访问问题
                field.setAccessible(true);
                try {
                    field.set(entry.getValue(), ioc.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    continue;
                }
            }
        }
    }

    private void doInstance() {
        if (classNames.isEmpty()) {
            return;
        }

        for (String className : classNames) {
            try {
                //反射创建类
                Class c = Class.forName(className);
                String beanName = toLowerFirstCase(c.getSimpleName());
                if (c.isInterface()) {
                    continue;
                }
                Object isntance = c.newInstance();
                if (c.isAnnotationPresent(ZHXController.class)) {
                    ioc.put(beanName, isntance);
                } else if (c.isAnnotationPresent(ZHXService.class)) {
                    //存在自定义实现类的名字
                    ZHXService zhxService = (ZHXService) c.getAnnotation(ZHXService.class);
                    if (!"".equals(beanName)) {
                        beanName = zhxService.value();
                    }
                    ioc.put(beanName, isntance);
                    //处理service把实现类赋值给controller层的接口变量
                    for (Class anInterface : c.getInterfaces()) {
                        String ibeanName = anInterface.getName();
                        //接口有多个实现类则抛出异常
                        if (ioc.containsKey(ibeanName)) {
                            throw new RuntimeException(ibeanName + "存在多个实现类!");
                        }
                        ioc.put(ibeanName, isntance);
                    }
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
    }

    private String toLowerFirstCase(String simpleName) {
        char[] chars = simpleName.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

    private void doScanner(String scanPackage) {
        //把com.xxx.xxx改为com/xxx/xxx
        URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
        File classPath = new File(url.getFile());
        for (File file : classPath.listFiles()) {
            if (file.isDirectory()) {
                doScanner(scanPackage + "." + file.getName());
            } else {
                if (!file.getName().endsWith(".class")) {
                    continue;
                }
                String className = scanPackage + "." + file.getName().replaceAll(".class", "");
                classNames.add(className);
            }
        }
    }

    private void doLoadConfig(String contextConfig) {
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfig);
        try {
            properties.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null == is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
